package com.luych.cloud.database.comparetor;

import com.luych.cloud.database.comparetor.core.Column;
import com.luych.cloud.database.comparetor.core.Connection;
import com.luych.cloud.database.comparetor.core.Database;
import com.luych.cloud.database.comparetor.core.Table;
import com.luych.cloud.database.comparetor.dm.DmConnection;
import com.luych.cloud.database.comparetor.dm.DmDataType;
import com.luych.cloud.database.comparetor.mysql.MysqlConnection;
import com.luych.cloud.database.comparetor.mysql.MysqlDataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@SpringBootApplication
@RestController
public class DataBaseCompartorApplication {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataBaseCompartorApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(DataBaseCompartorApplication.class, args);
    }

    private Connection getConnection(String driver, String url, String username, String password) {
        if ("mysql".equalsIgnoreCase(driver)) {
            return new MysqlConnection(url, username, password);
        } else if ("dm".equalsIgnoreCase(driver)) {
            return new DmConnection(url, username, password);
        } else {
            throw new UnsupportedOperationException();
        }
    }

    @PostMapping("/ping")
    public void ping(HttpSession session) {
        session.setAttribute("session_id", session.getId());
    }

    @PostMapping("/connect/{uuid}")
    public List<String> connect(@PathVariable String uuid,
                                @RequestParam String driver,
                                @RequestParam String host,
                                @RequestParam int port,
                                @RequestHeader String username,
                                @RequestHeader String password,
                                HttpSession session) {
        String url = "jdbc:" + driver + "://" + host + ":" + port;
        Connection connection = getConnection(driver, url, username, password);
        session.setAttribute("connection-" + uuid, connection);
        return connection.getDatabaseNames();
    }

    @GetMapping("/connect/{uuid}/{database}")
    public Database database(@PathVariable String uuid,
                             @PathVariable String database,
                             HttpSession session) {
        Connection connection = (Connection) session.getAttribute("connection-" + uuid);
        if (connection == null) throw new IllegalArgumentException("Unknown connection uuid: " + uuid);
        Database result = connection.getDatabase(database);
        session.setAttribute("database-" + uuid, result);
        return result;
    }

    @GetMapping("/{driver}/data-type")
    public List<String> dataTypes(@PathVariable String driver) {
        if ("mysql".equalsIgnoreCase(driver)) {
            return Arrays.stream(MysqlDataType.values()).map(Enum::name).collect(Collectors.toList());
        } else if ("dm".equalsIgnoreCase(driver)) {
            return Arrays.stream(DmDataType.values()).map(Enum::name).collect(Collectors.toList());
        } else {
            throw new UnsupportedOperationException();
        }
    }

    @GetMapping("/from/{suid}/{name}/to/{tuid}")
    public String sql(@PathVariable String suid,
                      @PathVariable String name,
                      @PathVariable String tuid,
                      HttpSession session) {
        Database sourceDatabase = (Database) session.getAttribute("database-" + suid);
        if (sourceDatabase == null) throw new IllegalArgumentException("Unknown connection database: " + suid);
        Database targetDatabase = (Database) session.getAttribute("database-" + tuid);
        if (targetDatabase == null) throw new IllegalArgumentException("Unknown connection database: " + tuid);

        if (name.contains(".")) {
            String tableName = name.split("\\.")[0];
            String columnName = name.split("\\.")[1];
            Table sourceTable = sourceDatabase.getTables().stream().filter(st -> tableName.equalsIgnoreCase(st.getName()))
                    .findFirst().orElseThrow(IllegalArgumentException::new);
            Table targetTable = targetDatabase.getTables().stream().filter(tt -> tableName.equalsIgnoreCase(tt.getName()))
                    .findFirst().orElseThrow(IllegalArgumentException::new);
            Optional<Column> sourceColumn = sourceTable.getColumns().stream().filter(sc -> columnName.equalsIgnoreCase(sc.getName())).findFirst();
            Optional<Column> targetColumn = targetTable.getColumns().stream().filter(sc -> columnName.equalsIgnoreCase(sc.getName())).findFirst();
            if (sourceColumn.isPresent() && targetColumn.isPresent()) {
                return targetTable.getModifyVolumeSql(tableName, sourceColumn.get());
            } else if (sourceColumn.isPresent()) {
                return targetTable.getCreateVolumeSql(tableName, sourceColumn.get());
            } else if (targetColumn.isPresent()) {
                return targetTable.getRemoveVolumeSql(tableName, columnName);
            } else {
                throw new IllegalArgumentException("Unknown column: " + columnName);
            }
        } else {
            String tableName = name;
            Optional<Table> sourceTable = sourceDatabase.getTables().stream().filter(st -> tableName.equalsIgnoreCase(st.getName())).findFirst();
            Optional<Table> targetTable = targetDatabase.getTables().stream().filter(st -> tableName.equalsIgnoreCase(st.getName())).findFirst();
            if (sourceTable.isPresent() && targetTable.isPresent()) {
                return "";
            } else if (sourceTable.isPresent()) {
                return targetDatabase.getCreateTableSql(sourceTable.get());
            } else if (targetTable.isPresent()) {
                return targetDatabase.getRemoveTableSql(tableName);
            } else {
                throw new IllegalArgumentException("Unknown table: " + tableName);
            }
        }
    }

    @PostMapping("/connect/{uuid}/sql")
    private void exec(@PathVariable String uuid, @RequestBody String sql, HttpSession session) {
        Connection connection = (Connection) session.getAttribute("connection-" + uuid);
        if (connection == null) throw new IllegalArgumentException("Unknown connection uuid: " + uuid);
        Database database = (Database) session.getAttribute("database-" + uuid);
        if (database == null) throw new IllegalArgumentException("Unknown connection database: " + uuid);
        LOGGER.info("Exec SQL: " + sql);
        connection.execute(database.getName(), sql);
    }
}
